package co.edu.unal.bioingenium.kbmed.text.index.io;

import co.edu.unal.bioingenium.kbmed.config.Configuration;
import co.edu.unal.bioingenium.kbmed.text.index.structures.InvertedIndexIntToInt;
import co.edu.unal.bioingenium.kbmed.text.index.structures.InvertedIndexIntToStr;
import co.edu.unal.bioingenium.kbmed.text.index.structures.InvertedIndexStrToInt;
import co.edu.unal.bioingenium.kbmed.text.index.structures.InvertedIndexStrToStr;
import co.edu.unal.bioingenium.kbmed.text.index.structures.InvertedIndexStrToWordInContext;

import gnu.trove.iterator.TShortIterator;
import gnu.trove.set.hash.TShortHashSet;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * 
 * @author Alejandro Riveros Cruz Riveros Cruz
 */
public class InvertedIndexIO {

    private static final String FIELD_SEPARATOR = ":";
    private static final String ENTRY_SEPARATOR = ",";

    /**
     * word:conceptID,idx1,idx2,:conceptId,idx1,idx2,
     * word:conceptID,idx1,idx2,
     * @param invertedIndex
     * @param name
     * @throws IOException
     */
    public static void storeStrToWordInContextInvertedIndex(InvertedIndexStrToWordInContext invertedIndex, String name) {
        FileWriter fileWriter = null;
        try {
            File file = new File(Configuration.INDEX_DIR + File.separator + name);
            if (!file.exists()) {
                file.createNewFile();
            }
            fileWriter = new FileWriter(file);
            BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
            Set<String> keyWordsSet = invertedIndex.getIndex().keySet();
            Map<String, TShortHashSet> contextMap;
            TShortHashSet contextIds;
            for (String word : keyWordsSet) {
                bufferedWriter.write(word + FIELD_SEPARATOR);
                contextMap = invertedIndex.getElement(word);
                for (String contextId : contextMap.keySet()) {
                    bufferedWriter.write(contextId + ENTRY_SEPARATOR);
                    contextIds = invertedIndex.getElement(word).get(contextId);
                    for (TShortIterator it = contextIds.iterator(); it.hasNext();) {
                        bufferedWriter.write(it.next() + ENTRY_SEPARATOR);
                    }
                    bufferedWriter.write(FIELD_SEPARATOR);
                }
                bufferedWriter.newLine();
            }
            bufferedWriter.flush();
            bufferedWriter.close();
        } catch (IOException ex) {
            Logger.getLogger(InvertedIndexIO.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            try {
                fileWriter.close();
            } catch (IOException ex) {
                Logger.getLogger(InvertedIndexIO.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    /**
     *
     *
     * @param name
     * @return 
     * @throws IOException  
     */
    public static InvertedIndexStrToWordInContext loadStrToWordInContextInvertedIndex(String name) {
        FileReader fileReader = null;
        try {
            InvertedIndexStrToWordInContext invertedIndex = new InvertedIndexStrToWordInContext();
            File file = new File(Configuration.INDEX_DIR + File.separator + name);
            fileReader = new FileReader(file);
            BufferedReader bufferedReader = new BufferedReader(fileReader);
            String line;
            StringTokenizer fieldTokenizer;
            StringTokenizer entryTokenizer;
            String word, conceptId;
            while ((line = bufferedReader.readLine()) != null) {
                fieldTokenizer = new StringTokenizer(line, FIELD_SEPARATOR);
                word = fieldTokenizer.nextToken();
                while (fieldTokenizer.hasMoreTokens()) {
                    entryTokenizer = new StringTokenizer(fieldTokenizer.nextToken(), ENTRY_SEPARATOR);
                    conceptId = entryTokenizer.nextToken();
                    while (entryTokenizer.hasMoreTokens()) {
                        invertedIndex.addElement(word, conceptId, Short.valueOf(entryTokenizer.nextToken()));
                    }
                }
            }
            bufferedReader.close();
            return invertedIndex;
        } catch (IOException ioex) {
            Logger.getLogger(InvertedIndexIO.class.getName()).log(Level.SEVERE, null, ioex);
        } finally {
            try {
                fileReader.close();
            } catch (IOException ex) {
                Logger.getLogger(InvertedIndexIO.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        return null;
    }

    /**
     * 
     * @param invertedIndex
     * @param name
     * @throws IOException
     */
    public static void storeStrToStrInvertedIndex(InvertedIndexStrToStr invertedIndex, String name) {
        FileWriter fileWriter = null;
        try {
            File file = new File(Configuration.INDEX_DIR + File.separator + name);
            if (!file.exists()) {
                file.createNewFile();
            }
            fileWriter = new FileWriter(file);
            BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
            Set<String> keySet = invertedIndex.getIndex().keySet();
            Set<String> elements;
            for (String key : keySet) {
                bufferedWriter.write(key + FIELD_SEPARATOR);
                elements = invertedIndex.getElement(key);
                for (String element : elements) {
                    bufferedWriter.write(element + FIELD_SEPARATOR);
                }
                bufferedWriter.newLine();
            }
            bufferedWriter.flush();
            bufferedWriter.close();
        } catch (IOException ex) {
            Logger.getLogger(InvertedIndexIO.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            try {
                fileWriter.close();
            } catch (IOException ex) {
                Logger.getLogger(InvertedIndexIO.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

    }

    /**
     *
     *
     * @param name
     * @return 
     * @throws IOException  
     */
    public static InvertedIndexStrToStr loadStrToStrInvertedIndex(String name) {
        FileReader fileReader = null;
        try {
            InvertedIndexStrToStr invertedIndex = new InvertedIndexStrToStr();
            File file = new File(Configuration.INDEX_DIR + File.separator + name);
            fileReader = new FileReader(file);
            BufferedReader bufferedReader = new BufferedReader(fileReader);
            String line;
            StringTokenizer tokenizer;
            String key;
            while ((line = bufferedReader.readLine()) != null) {
                tokenizer = new StringTokenizer(line, FIELD_SEPARATOR);
                key = tokenizer.nextToken();
                while (tokenizer.hasMoreTokens()) {
                    invertedIndex.addElement(key, tokenizer.nextToken());
                }
            }
            bufferedReader.close();
            return invertedIndex;
        } catch (IOException ioex) {
            Logger.getLogger(InvertedIndexIO.class.getName()).log(Level.SEVERE, null, ioex);
        } finally {
            try {
                fileReader.close();
            } catch (IOException ex) {
                Logger.getLogger(InvertedIndexIO.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        return null;
    }

    /**
     *
     *
     * @param invertedIndex
     * @param name 
     * @throws IOException  
     */
    public static void storeStrToIntInvertedIndex(InvertedIndexStrToInt invertedIndex, String name) {
        FileWriter fileWriter = null;
        try {
            File file = new File(Configuration.INDEX_DIR + File.separator + name);
            if (!file.exists()) {
                file.createNewFile();
            }
            fileWriter = new FileWriter(file);
            BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
            Set<String> keySet = invertedIndex.getIndex().keySet();
            Set<Integer> elements;
            for (String key : keySet) {
                bufferedWriter.write(key + FIELD_SEPARATOR);
                elements = invertedIndex.getElement(key);
                for (Integer element : elements) {
                    bufferedWriter.write(element + FIELD_SEPARATOR);
                }
                bufferedWriter.newLine();
            }
            bufferedWriter.flush();
            bufferedWriter.close();
        } catch (IOException ex) {
            Logger.getLogger(InvertedIndexIO.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            try {
                fileWriter.close();
            } catch (IOException ex) {
                Logger.getLogger(InvertedIndexIO.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

    }

    /**
     *
     *
     * @param name 
     * @return
     * @throws IOException  
     */
    public static InvertedIndexStrToInt loadStrToIntInvertedIndex(String name) {
        FileReader fileReader = null;
        try {
            InvertedIndexStrToInt invertedIndex = new InvertedIndexStrToInt();
            File file = new File(Configuration.INDEX_DIR + File.separator + name);
            fileReader = new FileReader(file);
            BufferedReader bufferedReader = new BufferedReader(fileReader);
            String line;
            StringTokenizer tokenizer;
            String key;
            while ((line = bufferedReader.readLine()) != null) {
                tokenizer = new StringTokenizer(line, FIELD_SEPARATOR);
                key = tokenizer.nextToken();
                while (tokenizer.hasMoreTokens()) {
                    invertedIndex.addElement(key, Integer.parseInt(tokenizer.nextToken()));
                }
            }
            bufferedReader.close();
            return invertedIndex;
        } catch (IOException ioex) {
            Logger.getLogger(InvertedIndexIO.class.getName()).log(Level.SEVERE, null, ioex);
        } finally {
            try {
                fileReader.close();
            } catch (IOException ex) {
                Logger.getLogger(InvertedIndexIO.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        return null;
    }

    /**
     *
     *
     * @param invertedIndex 
     * @param name
     * @throws IOException  
     */
    public static void storeIntToStrInvertedIndex(InvertedIndexIntToStr invertedIndex, String name) {
        FileWriter fileWriter = null;
        try {
            File file = new File(Configuration.INDEX_DIR + File.separator + name);
            if (!file.exists()) {
                file.createNewFile();
            }
            fileWriter = new FileWriter(file);
            BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
            Set<Integer> keySet = invertedIndex.getIndex().keySet();
            Set<String> elements;
            for (Integer key : keySet) {
                bufferedWriter.write(key + FIELD_SEPARATOR);
                elements = invertedIndex.getElement(key);
                for (String element : elements) {
                    bufferedWriter.write(element + FIELD_SEPARATOR);
                }
                bufferedWriter.newLine();
            }
            bufferedWriter.flush();
            bufferedWriter.close();
        } catch (IOException ex) {
            Logger.getLogger(InvertedIndexIO.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            try {
                fileWriter.close();
            } catch (IOException ex) {
                Logger.getLogger(InvertedIndexIO.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

    }

    /**
     *
     *
     * @param name 
     * @return 
     * @throws IOException 
     */
    public static InvertedIndexIntToStr loadIntToStrInvertedIndex(String name) {
        FileReader fileReader = null;
        try {
            InvertedIndexIntToStr invertedIndex = new InvertedIndexIntToStr();
            File file = new File(Configuration.INDEX_DIR + File.separator + name);
            fileReader = new FileReader(file);
            BufferedReader bufferedReader = new BufferedReader(fileReader);
            String line;
            StringTokenizer tokenizer;
            Integer key;
            while ((line = bufferedReader.readLine()) != null) {
                tokenizer = new StringTokenizer(line, FIELD_SEPARATOR);
                key = Integer.parseInt(tokenizer.nextToken());
                while (tokenizer.hasMoreTokens()) {
                    invertedIndex.addElement(key, tokenizer.nextToken());
                }
            }
            bufferedReader.close();
            return invertedIndex;
        } catch (IOException ioex) {
            Logger.getLogger(InvertedIndexIO.class.getName()).log(Level.SEVERE, null, ioex);
        } finally {
            try {
                fileReader.close();
            } catch (IOException ex) {
                Logger.getLogger(InvertedIndexIO.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        return null;
    }

    /**
     *
     *
     * @param invertedIndex 
     * @param name
     * @throws IOException  
     */
    public static void storeIntToIntInvertedIndex(InvertedIndexIntToInt invertedIndex, String name) {
        FileWriter fileWriter = null;
        try {
            File file = new File(Configuration.INDEX_DIR + File.separator + name);
            if (!file.exists()) {
                file.createNewFile();
            }
            fileWriter = new FileWriter(file);
            BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
            Set<Integer> keySet = invertedIndex.getIndex().keySet();
            Set<Integer> elements;
            for (Integer key : keySet) {
                bufferedWriter.write(key + FIELD_SEPARATOR);
                elements = invertedIndex.getElement(key);
                for (Integer element : elements) {
                    bufferedWriter.write(element + FIELD_SEPARATOR);
                }
                bufferedWriter.newLine();
            }
            bufferedWriter.flush();
            bufferedWriter.close();
        } catch (IOException ex) {
            Logger.getLogger(InvertedIndexIO.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            try {
                fileWriter.close();
            } catch (IOException ex) {
                Logger.getLogger(InvertedIndexIO.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

    }

    /**
     *
     *
     * @param name
     * @return
     * @throws IOException  
     */
    public static InvertedIndexIntToInt loadIntToIntInvertedIndex(String name) {
        FileReader fileReader = null;
        try {
            InvertedIndexIntToInt invertedIndex = new InvertedIndexIntToInt();
            File file = new File(Configuration.INDEX_DIR + File.separator + name);
            fileReader = new FileReader(file);
            BufferedReader bufferedReader = new BufferedReader(fileReader);
            String line;
            StringTokenizer tokenizer;
            Integer key;
            while ((line = bufferedReader.readLine()) != null) {
                tokenizer = new StringTokenizer(line, FIELD_SEPARATOR);
                key = Integer.parseInt(tokenizer.nextToken());
                while (tokenizer.hasMoreTokens()) {
                    invertedIndex.addElement(key, Integer.parseInt(tokenizer.nextToken()));
                }
            }
            bufferedReader.close();
            return invertedIndex;
        } catch (IOException ex) {
            Logger.getLogger(InvertedIndexIO.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            try {
                fileReader.close();
            } catch (IOException ex) {
                Logger.getLogger(InvertedIndexIO.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        return null;
    }

    /**
     * 
     * @param args
     * @throws IOException
     */
    public static void main(String[] args) throws IOException {

        InvertedIndexStrToWordInContext iistwic = new InvertedIndexStrToWordInContext();

        iistwic.addElement("word", "c1", (short) 1);
        iistwic.addElement("word", "c2", (short) 1);
        iistwic.addElement("word", "c2", (short) 1);
        iistwic.addElement("word", "c2", (short) 2);
        iistwic.addElement("word2", "c1", (short) 1);
        iistwic.addElement("word2", "c2", (short) 1);
        iistwic.addElement("word2", "c2", (short) 2);
        iistwic.addElement("word2", "c3", (short) 1);
        iistwic.printIndexCardinallity();
        storeStrToWordInContextInvertedIndex(iistwic, "testidx");
        InvertedIndexStrToWordInContext iistwic2 = loadStrToWordInContextInvertedIndex("testidx");
        iistwic2.printIndexCardinallity();
    }
}
